#include "CPU.h"
#include "Program.h"
#include "ULA.h"
#include "Decoder.h"
#include "ConstantCU.h"
#include "MemoryAU.h"
#include "ControlTU.h"
#include <iostream>
#include <fstream>

using namespace std;

CPU::CPU()
: running(true),
 PC(0),
 IR(0),
 flag_carry(false),
 flag_neg(false),
 flag_negzero(false),
 flag_overflow(false),
 flag_true(true),
 flag_zero(false),
 screenMode(false),
 startDumpPos(0),
 dumpAmount(0),
 pausePrint(false)

{
	Decoder::init(this);
	ULA::setCPU(this);
	ConstantCU::setCPU(this);
	ControlTU::setCPU(this);
	MemoryAU::setCPU(this);
	for(int i=0;i<8;i++)
	{
		registers[i]=0;
		memory[i]=0;
	}
	for(int i=8;i<65536;i++)
		memory[i]=0;
}

CPU::~CPU()
{
}
unsigned short CPU::readRegister(int pos)
{
	return registers[pos];
}
void CPU::setRegister(int pos, short value)
{
	registers[pos] = value;
}
unsigned short CPU::readMemory(int pos)
{
	return memory[pos];
}
void CPU::setMemory(int pos,short value)
{
	memory[pos] = value;
}
void CPU::setFlagNeg(bool b)
{
	flag_neg = b;
}
void CPU::setFlagZero(bool b)
{
	flag_zero = b;
}
void CPU::setFlagNegZero(bool b)
{
	flag_negzero = b;
}
void CPU::setFlagCarry(bool b)
{
	flag_carry = b;
}
void CPU::setFlagOverflow(bool b)
{
	flag_overflow = b;
}
void CPU::setFlagTrue(bool b)
{
	flag_true = b;
}
bool CPU::getFlagNeg()
{
	return flag_neg;
}
bool CPU::getFlagZero()
{
	return flag_zero;
}
bool CPU::getFlagNegZero()
{
	return (flag_zero | flag_neg);
}
bool CPU::getFlagCarry()
{
	return flag_carry;
}
bool CPU::getFlagOverflow()
{
	return flag_overflow;
}
bool CPU::getFlagTrue()
{
	return flag_true;
}
void CPU::setIR(unsigned short value)
{
	IR = value;
}
void CPU::setPC(unsigned short value)
{
//	cout << "vou mudar o PC para " << value << endl;
	PC = value;
}
unsigned short CPU::getIR()
{
	return IR;
}
unsigned short CPU::getPC()
{
	return PC;
}

void CPU::printCPUStatus()
{
	cout << "-----------------------------------------------------------------------" << endl;
	cout << endl;
	cout << "Status do Processador" << endl;
	cout << "REGISTRADORES		FLAGS" << endl;
	cout << endl;
	cout << "R0 = " << hex << readRegister(0) << "		NEG = " << getFlagNeg() << endl;
	cout << endl;
	cout << "R1 = " << hex << readRegister(1) << "		ZERO = " << getFlagZero() << endl;
	cout << endl;
	cout << "R2 = " << hex << readRegister(2) << "		CARRY = " << getFlagCarry() << endl;
	cout << endl;
	cout << "R3 = " << hex << readRegister(3) << "		NEGZERO = " << getFlagNegZero() << endl;
	cout << endl;
	cout << "R4 = " << hex << readRegister(4) << "		OVERFLOW = " << getFlagOverflow() << endl;
	cout << endl;
	cout << "R5 = " << hex << readRegister(5) << endl;
	cout << endl;
	cout << "R6 = " << hex << readRegister(6) << endl;
	cout << endl;
	cout << "R7 = " << hex << readRegister(7) << endl;
	cout << endl;
	cout << "PC = " << hex << getPC() << endl;
	cout << endl;
	cout << "IR = " << hex << getIR() << endl;
	cout << endl;
	cout << "-----------------------------------------------------------------------" << endl;
	
}


void CPU::instructionFetch() {
	setIR( readMemory(getPC()) );
}

void CPU::instructionDecode() {
	func = Decoder::decode();
}

void CPU::execute() { 
	if (func != NULL)
		(*func)();
}

void CPU::writeBack() {
	if(shallWB)
	{
		setRegister(WBRegister,WBValue);
		setshallWB(false);
	}
}


void CPU::run() {
	while (running) {
		instructionFetch();
		PC++;
		instructionDecode();
		execute();
		writeBack();
		if(getScreenMode())
		{
			printCPUStatus();
			if(getPausePrint())
				getchar();
		}
	}
	saveMemory();
}
void CPU::halt() {
	running = false;
}

void CPU::saveMemory() {
/*	ofstream file;
	file.open("mem.txt",fstream::out | fstream::in);
	for(int i =0;i< 65536; i++)
		file << "POS: " << i << "  VALUE: " << hex << memory[i] << endl;
	file.close();	*/
	int i=0;
	int j=0;
	for(int i = getStartDumpPos(); (i<(getStartDumpPos()+ getDumpAmount()));i++)
	{
		if(i < 65536)
			cout << "POS: " << i << "  VALUE: " << hex << memory[i] << endl;
		else
			break;
		
		j = (++j%25);
		if(j==24 && getPausePrint())
			getchar();
	}
}

void CPU::setWBRegister(unsigned short reg)
{
	WBRegister = reg;
}
void CPU::setWBValue(short WBvalue)
{
	WBValue = WBvalue; 
}
void CPU::setshallWB(bool wb)
{
	shallWB = wb;
}

void CPU::setScreenMode(bool sm)
{
	screenMode = sm;
}
bool CPU::getScreenMode()
{
	return screenMode;
}
void CPU::setStartDumpPos(int dp)
{
	startDumpPos = dp;
}
int CPU::getStartDumpPos()
{
	return startDumpPos;
}
void CPU::setDumpAmount(int da)
{
	dumpAmount = da;
}
int CPU::getDumpAmount()
{
	return dumpAmount;
}
void CPU::setPausePrint(bool pp)
{
	pausePrint = pp;
}
bool CPU::getPausePrint()
{
	return pausePrint;
}
